import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { winstonInstance, createStartupLogger, errorLogger } from "./config/winston.config";
import { WinstonModule } from "nest-winston";
import { ValidationPipe } from "@nestjs/common";
import helmet from "helmet";
import rateLimit from "express-rate-limit";

/**
 * 应用程序启动函数
 * 这是 NestJS 应用的入口点，负责创建和启动应用实例
 */
async function bootstrap() {
  const logger = WinstonModule.createLogger({
    instance: winstonInstance,
  });
  // 使用 NestFactory 创建应用实例
  // NestFactory 是 NestJS 的核心工厂类，用于创建应用上下文
  // AppModule 是根模块，包含了整个应用的模块依赖关系
  const app = await NestFactory.create(AppModule, {
    // logger: ['error', 'warn']
    // logger: true
    logger,
    // 允许跨域请求
    cors: true,
  });

  // 设置全局前缀
  app.setGlobalPrefix("/api/v1/");

  // 全局管道
  app.useGlobalPipes(
    new ValidationPipe({
      // 去除在类上不存在的字段
      // whitelist: true,
    }),
  );

  // helmet 是一个 npm 包，它提供了许多安全相关的中间件
  // 可以帮助保护应用程序免受一些常见的攻击
  app.use(helmet());

  // rateLimit 限流
  app.use(
    rateLimit({
      windowMs: 1 * 60 * 1000, // 15 minutes
      max: 300, // 将每个 IP 的请求限制为每 “窗口时间（windowMs）”100 次。s
    }),
  );

  // 从环境变量获取端口号，如果未设置则默认使用 3000
  // 这是生产环境部署的最佳实践，允许外部配置端口
  const port = process.env.PORT || 3000;

  // 启动 HTTP 服务器，开始监听指定端口的请求
  // listen() 方法返回一个 Promise，确保服务器完全启动后再继续
  await app.listen(port);

  // 使用美观的日志输出应用启动信息
  winstonInstance.info("NestJS 应用启动成功", {
    context: "Bootstrap",
    port: port,
    url: `http://localhost:${port}`,
    environment: process.env.NODE_ENV || "development",
    timestamp: new Date().toISOString(),
  });

  // 热重载支持 (Hot Module Replacement - HMR)
  // 这部分代码只在开发环境下生效，用于支持代码热更新
  if (module.hot) {
    // 接受模块热更新
    module.hot.accept();
    // 当模块被替换时，优雅地关闭当前应用实例
    module.hot.dispose(() => app.close());
  }

  return `http://localhost:${port}`;
}

// 启动应用并处理启动结果
// 使用 .then() 确保启动过程中的任何异步操作都能正确完成
bootstrap()
  .then((url) => {
    // 创建一个临时的winston实例用于启动成功日志
    const startupLogger = createStartupLogger(url);

    startupLogger.info("服务器启动成功");
  })
  .catch((error) => {
    errorLogger.error("应用启动失败", error);
    process.exit(1); // 启动失败时退出进程
  });
